package com.cefetmg.mmc.mrcpsp.process.impl;

import java.util.List;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import com.cefetmg.mmc.mrcpsp.model.main.Job;
import com.cefetmg.mmc.mrcpsp.model.main.Mode;
import com.cefetmg.mmc.mrcpsp.model.main.Project;
import com.cefetmg.mmc.mrcpsp.model.main.ResourceAvailabilities;
import com.cefetmg.mmc.mrcpsp.process.RestrictionsProcessor;
import com.cefetmg.mmc.mrcpsp.utils.LogUtils;
import com.cefetmg.mmc.mrcpsp.utils.PropertyConstants;

public class RestrictionsProcessorImpl implements RestrictionsProcessor {

	private static final Logger log = Logger.getLogger(RestrictionsProcessorImpl.class);
	
	@Override
	public boolean checkNonRenewableResourcesAmount(Project project) {
		ResourceAvailabilities ra = project.getResourceAvailabilities();
		List<Job> jobs = project.getStaggeredJobs();
		boolean checkAmount = true;
		
		log.info("Checking the Non Renewable resources amount. Initial Amount: " + ra.getNonRenewableInitialAmount());
		for (Job job: jobs) {
			Mode mode = job.getMode();
			
			checkAmount = setNonRenewableResourcesConsumedAmount(ra, mode);
			
			if (checkAmount == false) {				
				log.log(Level.ERROR, "FILE instance: "  + project.getFileName() + " .Problem calculating the non renewable resources amount of the job " + job.getId() + ".");			
				return checkAmount;
			} 
			
			log.debug("Consumed non renewable amounts after job " + job.getId() + " execution will be: " + ra.getNonRenewableConsumedAmount());
		}
		
		log.info("Consumed non renewable amounts after jobs execution will be: " + ra.getNonRenewableConsumedAmount());
		log.info("Remaining non renewable amounts after jobs execution will be: " + ra.getRemainingNonRenewableAmount());
		return checkAmount;
	}
	
	private boolean setNonRenewableResourcesConsumedAmount(ResourceAvailabilities ra, Mode mode) {
		Integer count = 0;
		boolean checkAmount = true;
		
		for (Integer amount : mode.getNonRenewable()) {
			Integer nonRenewableAmount = ra.getNonRenewableConsumedAmount().get(count);
			Integer remainingResources = 0;			
			nonRenewableAmount+= amount;
			ra.getNonRenewableConsumedAmount().set(count, nonRenewableAmount);			
			
			remainingResources = ra.getNonRenewableInitialAmount().get(count) - ra.getNonRenewableConsumedAmount().get(count);
			ra.getRemainingNonRenewableAmount().set(count, remainingResources);
			
			if (remainingResources < 0) {
				checkAmount = false;
			}
			
			count++;
		}	
		
		return checkAmount;
	}	

	@Override
	public boolean setRenewableResourcesConsumedAmount(ResourceAvailabilities ra, Mode mode, Integer operation) {		
		boolean checkAmount = false;
		
		checkAmount = checkResourcesBeforeSetValues(ra, mode, operation, checkAmount);
		
		if (checkAmount) {
			checkResourcesBeforeSetValues(ra, mode, operation, checkAmount);
		}
		
		return checkAmount;
	}
	
	private boolean checkResourcesBeforeSetValues(ResourceAvailabilities ra, Mode mode, Integer operation, boolean setValues) {
		Integer count = 0;
		boolean checkAmount = true;
		
		for (Integer amount : mode.getRenewable()) {
			Integer renewableAmount = ra.getRenewableConsumedAmount().get(count);
			Integer remainingResources = 0;			
			
			if (operation == PropertyConstants.ADD) {
				renewableAmount+= amount;
			} else if (operation == PropertyConstants.SUBTRACT) {
				renewableAmount-= amount;
			} else {
				log.log(Level.ERROR, "Operation not supported!" + LogUtils.generateErrorLog(Thread.currentThread().getStackTrace()));
				throw new IllegalArgumentException("Operation not supported!" + LogUtils.generateErrorLog(Thread.currentThread().getStackTrace()));
			}
			
			remainingResources = ra.getRenewableInitialAmount().get(count) - renewableAmount;
			
			if (remainingResources < 0) {
				return checkAmount = false;				
			} 
			
			if (setValues) {
				ra.getRemainingRenewableAmount().set(count, remainingResources);
				ra.getRenewableConsumedAmount().set(count, renewableAmount);
			}
			
			count++;			
		}	
		
		return checkAmount;
	}

	@Override
	public Job getBestJobToRemoveRR(ResourceAvailabilities ra, List<Job> jobs, Job jobToScheduled) {
		Integer newAmount;
		Integer countJob = 0;
		boolean checkJob = false;
		Job job = null;	
		
		while (countJob < jobs.size() && !checkJob) {
			Integer countMode = 0;
			boolean checkAmount = false;
			boolean checkExecution = true;
			
			job = jobs.get(countJob);
			
			while (countMode < job.getMode().getRenewable().size() && checkExecution) {
				newAmount = ra.getRemainingRenewableAmount().get(countMode) + job.getMode().getRenewable().get(countMode);				
				
				if (jobToScheduled.getMode().getRenewable().get(countMode) <= newAmount) {
					checkAmount = true;
				} else {
					checkAmount = false;
					checkExecution = false;
				}				
				
				countMode++;
			}
			
			if (checkAmount && checkExecution) {
				checkJob = true;
				return job;
			}
			
			countJob++;
		}
		
		return null;
	}
	

}
